package com.anti.modular.sys.menu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.anti.commom.constant.SymbolConstant;
import com.anti.commom.core.exception.BusinessException;
import com.anti.commom.pojo.node.LoginMenuTreeNode;
import com.anti.core.context.login.LoginContextHolder;
import com.anti.core.enums.MenuTypeEnum;
import com.anti.jdbc.DBUtils;
import com.anti.modular.sys.auth.factory.TreeBuildFactory;
import com.anti.modular.sys.menu.MenuOpenTypeEnum;
import com.anti.modular.sys.menu.dao.SysMenuDao;
import com.anti.modular.sys.menu.entity.SysMenu;
import com.anti.modular.sys.menu.enums.SysMenuExceptionEnum;
import com.anti.modular.sys.menu.node.MenuBaseTreeNode;
import com.anti.modular.sys.menu.param.SysMenuParam;
import com.anti.modular.sys.menu.service.SysMenuService;
import com.anti.modular.sys.role.service.SysRoleMenuService;
import com.anti.modular.sys.user.enums.SysUserAdminTypeEnum;
import com.anti.modular.sys.user.entity.SysUser;
import com.anti.modular.sys.user.service.SysUserRoleService;
import com.anti.modular.sys.user.service.SysUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @Description TODO
 * @Author anTi
 * @Date 2021-2-26
 */
@Service
public class SysMenuServiceImpl implements SysMenuService {
    @Resource
    DBUtils dbUtils;
    @Resource
    SysUserService sysUserService;
    @Resource
    SysMenuDao sysMenuDao;
    @Resource
    SysUserRoleService sysUserRoleService;
    @Resource
    SysRoleMenuService sysRoleMenuService;

    @Override
    public void add(SysMenuParam sysMenuParam) {
        // 校验参数
        checkParam(sysMenuParam, false);
        Long loginUserId = LoginContextHolder.me().getSysLoginUserId();
        SysMenu sysMenu = new SysMenu();
        BeanUtil.copyProperties(sysMenuParam, sysMenu);
        sysMenu.setCreateUser(loginUserId);
        sysMenu.setStatus(0);
        sysMenu.setCreateTime(new Date());
        // 设置新的pid
        String newPids = createNewPids(sysMenuParam.getPid());
        sysMenu.setPids(newPids);
        sysMenuDao.add(sysMenu);
    }

    @Override
    public void delete(SysMenuParam sysMenuParam) {
        sysMenuDao.delete(sysMenuParam.getId());
    }

    @Override
    public void edit(SysMenuParam sysMenuParam) {
        // 校验参数
        checkParam(sysMenuParam, true);
        // 获取修改的菜单的旧数据（库中的）
        SysMenu oldMenu = this.querySysMenu(sysMenuParam);

        // 本菜单旧的pids
        Long oldPid = oldMenu.getPid();
        String oldPids = oldMenu.getPids();

        // 生成新的pid和pids
        Long newPid = sysMenuParam.getPid();
        String newPids = this.createNewPids(sysMenuParam.getPid());
        // 是否更新子应用的标识
        boolean updateSubAppsFlag = false;

        // 是否更新子节点的pids的标识
        boolean updateSubPidsFlag = false;

        // 如果应用有变化
        if (!sysMenuParam.getApplication().equals(oldMenu.getApplication())) {
            // 父节点不是根节点不能移动应用
            if (!oldPid.equals(0L)) {
                throw new BusinessException(SysMenuExceptionEnum.CANT_MOVE_APP);
            }
            updateSubAppsFlag = true;
        }

        // 父节点有变化
        if (!newPid.equals(oldPid)) {
            updateSubPidsFlag = true;
        }

        // 开始更新所有子节点的配置
        if (updateSubAppsFlag || updateSubPidsFlag) {

            // 查找所有叶子节点，包含子节点的子节点
            String sql="select * from sys_menu where pids like '%"+oldMenu.getId()+"%'";
            List<SysMenu> list = dbUtils.query(sql,SysMenu.class);
            // 更新所有子节点的应用为当前菜单的应用
            if (ObjectUtil.isNotEmpty(list)) {
                // 更新所有子节点的application
                if (updateSubAppsFlag) {
                    list.forEach(child -> child.setApplication(sysMenuParam.getApplication()));
                }
                // 更新所有子节点的pids
                if (updateSubPidsFlag) {
                    list.forEach(child -> {
                        // 子节点pids组成 = 当前菜单新pids + 当前菜单id + 子节点自己的pids后缀
                        String oldParentCodesPrefix = oldPids + SymbolConstant.LEFT_SQUARE_BRACKETS + oldMenu.getId()
                                + SymbolConstant.RIGHT_SQUARE_BRACKETS + SymbolConstant.COMMA;
                        String oldParentCodesSuffix = child.getPids().substring(oldParentCodesPrefix.length());
                        String menuParentCodes = newPids + SymbolConstant.LEFT_SQUARE_BRACKETS + oldMenu.getId()
                                + SymbolConstant.RIGHT_SQUARE_BRACKETS + SymbolConstant.COMMA + oldParentCodesSuffix;
                        child.setPids(menuParentCodes);
                    });
                }

                updateBatchById(list);
            }
        }
        Long loginUserId = LoginContextHolder.me().getSysLoginUserId();
        SysMenu sysMenu = new SysMenu();
        sysMenu.setUpdateTime(new Date());
        sysMenu.setUpdateUser(loginUserId);
        BeanUtil.copyProperties(sysMenuParam, sysMenu);
        sysMenuDao.edit(sysMenu);
    }


    @Override
    public List<SysMenu> list(SysMenuParam sysMenuParam) {
        StringBuilder sb=new StringBuilder("select * from sys_menu where status=0  ");
        List<Object> params=new ArrayList<>();
        if (ObjectUtil.isNotNull(sysMenuParam)) {
            //根据所属应用查询
            if (ObjectUtil.isNotEmpty(sysMenuParam.getApplication())) {
                sb.append(" and application=?");
                params.add(sysMenuParam.getApplication());
            }
            //根据菜单名称模糊查询
            if (ObjectUtil.isNotEmpty(sysMenuParam.getName())) {
                sb.append(" and name=?");
                params.add(sysMenuParam.getName());
            }
        }
        sb.append(" order by sort asc");
        //根据排序升序排列，序号越小越在前
        List<SysMenu> sysMenuList = dbUtils.query(sb.toString(),SysMenu.class,params.toArray());
        //将结果集处理成树
        return new TreeBuildFactory<SysMenu>().doTreeBuild(sysMenuList);
    }

    @Override
    public List<String> getLoginPermissions(Long userId) {
        Set<String> permissions = CollectionUtil.newHashSet();
        List<Long> roleIdList = sysUserRoleService.getUserRoleIdList(userId);
        if (ObjectUtil.isNotEmpty(roleIdList)) {
            List<Long> menuIdList = sysRoleMenuService.getRoleMenuIdList(roleIdList);
            if (ObjectUtil.isNotEmpty(menuIdList)) {
                //按钮权限
                String sql="select permission from sys_menu where status=0 and type=2 and id in (:ids)";
                HashMap<Object, Object> param = CollectionUtil.newHashMap();
                param.put("ids",menuIdList);
                List<SysMenu> list = dbUtils.query(sql, SysMenu.class, param);
                list.forEach(sysMenu -> permissions.add(sysMenu.getPermission()));
            }
        }
        return CollectionUtil.newArrayList(permissions);
    }

    @Override
    public List<LoginMenuTreeNode> getLoginMenus(Long userId, String appCode) {
        List<SysMenu> sysMenuList;
        //如果是超级管理员则展示所有系统权重菜单
        SysUser sysUser = sysUserService.getById(userId);
        Integer adminType = sysUser.getAdminType();
        //非超级管理员则获取自己角色所拥有的菜单集合
        if (SysUserAdminTypeEnum.SUPER_ADMIN.getCode().equals(adminType)) {
            String menuSql="select * from sys_menu where status=0 and application=? and type<>2 order by sort asc";
            sysMenuList = dbUtils.query(menuSql,SysMenu.class,appCode);
            if (ObjectUtil.isNotEmpty(sysMenuList)) {
                //转换成登录菜单
                return this.convertSysMenuToLoginMenu(sysMenuList);
            }
            return CollectionUtil.newArrayList();
        }
        //获取用户角色id集合
        String sql="select id from sys_role  where status=0 and id in (select role_id from sys_user_role where user_id=?)";
        List<Long> roleIdList = dbUtils.query(sql, Long.class, userId);
        if (ObjectUtil.isNotEmpty(roleIdList)) {
            String menuSql="select * from sys_menu where status=0 and id in (select  menu_id from sys_role_menu where role_id in (:menuId)) and application=:appCode and type<>2 order by sort asc";
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put("menuId",roleIdList);
            paramMap.put("appCode",appCode);
            sysMenuList = dbUtils.query(menuSql,SysMenu.class,paramMap);
            if (ObjectUtil.isNotEmpty(sysMenuList)) {
                //转换成登录菜单
                return this.convertSysMenuToLoginMenu(sysMenuList);
            } else {
                //如果角色的菜单为空，则查不到菜单
                return CollectionUtil.newArrayList();
            }
        } else {
            //如果角色为空，则根本没菜单
            return CollectionUtil.newArrayList();
        }
    }


    @Override
    public List<MenuBaseTreeNode> tree(SysMenuParam sysMenuParam) {
        List<MenuBaseTreeNode> menuTreeNodeList = CollectionUtil.newArrayList();
        List<Object> asList = new ArrayList();
        StringBuilder sb=new StringBuilder("select * from sys_menu where status=0 ");
        if (ObjectUtil.isNotNull(sysMenuParam)) {
            if (ObjectUtil.isNotEmpty(sysMenuParam.getApplication())) {
                sb.append(" and application= ? ");
                asList.add(sysMenuParam.getApplication());
            }
        }
        sb.append(" and type<>2 order by sort asc ");
        List<SysMenu> menuList = dbUtils.query(sb.toString(), SysMenu.class, asList.toArray());
        menuList.forEach(sysMenu -> {
            MenuBaseTreeNode menuTreeNode = new MenuBaseTreeNode();
            menuTreeNode.setId(sysMenu.getId());
            menuTreeNode.setParentId(sysMenu.getPid());
            menuTreeNode.setValue(String.valueOf(sysMenu.getId()));
            menuTreeNode.setTitle(sysMenu.getName());
            menuTreeNode.setWeight(sysMenu.getSort());
            menuTreeNodeList.add(menuTreeNode);
        });
        return new TreeBuildFactory<MenuBaseTreeNode>().doTreeBuild(menuTreeNodeList);
    }


    @Override
    public List<MenuBaseTreeNode> treeForGrant(SysMenuParam sysMenuParam) {
        List<MenuBaseTreeNode> menuTreeNodeList = CollectionUtil.newArrayList();
        StringBuilder sb=new StringBuilder("select * from sys_menu where status=0 ");
        Map params=new HashMap();
        //根据应用查询
        if (ObjectUtil.isNotNull(sysMenuParam)) {
            if (ObjectUtil.isNotEmpty(sysMenuParam.getApplication())) {
                sb.append(" and application:application ");
                params.put("application",sysMenuParam.getApplication());
            }
        }
        //如果是超级管理员给角色授权菜单时可选择所有菜单
        if (!LoginContextHolder.me().isSuperAdmin()) {
            //非超级管理员则获取自己拥有的菜单，分配给人员，防止越级授权
            Long userId = LoginContextHolder.me().getSysLoginUserId();
            List<Long> roleIdList = sysUserRoleService.getUserRoleIdList(userId);
            if (ObjectUtil.isNotEmpty(roleIdList)) {
                List<Long> menuIdList = sysRoleMenuService.getRoleMenuIdList(roleIdList);
                if (ObjectUtil.isNotEmpty(menuIdList)) {
                    sb.append(" and id (:id) ");
                    params.put("id",menuIdList.toArray());
                } else {
                    //如果角色的菜单为空，则查不到菜单
                    return CollectionUtil.newArrayList();
                }
            } else {
                //如果角色为空，则根本没菜单
                return CollectionUtil.newArrayList();
            }
        }
        sb.append(" order by sort asc ");
        List<SysMenu> menus = dbUtils.query(sb.toString(), SysMenu.class, params);
        menus.forEach(sysMenu -> {
            MenuBaseTreeNode menuTreeNode = new MenuBaseTreeNode();
            menuTreeNode.setId(sysMenu.getId());
            menuTreeNode.setParentId(sysMenu.getPid());
            menuTreeNode.setValue(String.valueOf(sysMenu.getId()));
            menuTreeNode.setTitle(sysMenu.getName());
            menuTreeNode.setWeight(sysMenu.getSort());
            menuTreeNodeList.add(menuTreeNode);
        });
        return new TreeBuildFactory<MenuBaseTreeNode>().doTreeBuild(menuTreeNodeList);
    }

    public void updateBatchById(List<SysMenu> list) {
        sysMenuDao.updateBatchById(list);
    }

    /**
     * 获取系统菜单
     */
    private SysMenu querySysMenu(SysMenuParam sysMenuParam) {
        SysMenu sysMenu = this.getById(sysMenuParam.getId());
        if (ObjectUtil.isNull(sysMenu)) {
            throw new BusinessException(SysMenuExceptionEnum.MENU_NOT_EXIST);
        }
        return sysMenu;
    }

    private String createNewPids(Long pid) {
        if (pid.equals(0L)) {
            return SymbolConstant.LEFT_SQUARE_BRACKETS + 0 + SymbolConstant.RIGHT_SQUARE_BRACKETS
                    + SymbolConstant.COMMA;
        } else {
            //获取父菜单
            SysMenu parentMenu = this.getById(pid);
            return parentMenu.getPids()+ SymbolConstant.LEFT_SQUARE_BRACKETS + pid + SymbolConstant.RIGHT_SQUARE_BRACKETS
                    + SymbolConstant.COMMA;
        }
    }


    public SysMenu getById(Long id){
        String sql="select * from sys_menu where id=?";
        return dbUtils.getFirst(sql,SysMenu.class,id);
    }

    private void checkParam(SysMenuParam sysMenuParam, boolean b) {
        //菜单类型（字典 0目录 1菜单 2按钮）
        Integer type = sysMenuParam.getType();

        String router = sysMenuParam.getRouter();

        String permission = sysMenuParam.getPermission();

        Integer openType = sysMenuParam.getOpenType();

        if (MenuTypeEnum.DIR.getCode().equals(type)) {
            if (ObjectUtil.isEmpty(router)) {
                throw new BusinessException(SysMenuExceptionEnum.MENU_ROUTER_EMPTY);
            }
        }

        if (MenuTypeEnum.MENU.getCode().equals(type)) {
            if (ObjectUtil.isEmpty(router)) {
                throw new BusinessException(SysMenuExceptionEnum.MENU_ROUTER_EMPTY);
            }
            if (ObjectUtil.isEmpty(openType)) {
                throw new BusinessException(SysMenuExceptionEnum.MENU_OPEN_TYPE_EMPTY);
            }
        }

        if (MenuTypeEnum.BTN.getCode().equals(type)) {
            if (ObjectUtil.isEmpty(permission)) {
                throw new BusinessException(SysMenuExceptionEnum.MENU_PERMISSION_EMPTY);
            } else {
//                Set<String> urlSet = resourceCache.getAllResources();
//
//                if (!permission.contains(SymbolConstant.COLON)) {
//                    throw new BusinessException(SysMenuExceptionEnum.MENU_PERMISSION_ERROR);
//                }
//                permission = SymbolConstant.COLON + permission;
//                if (!urlSet.contains(permission.replaceAll(SymbolConstant.COLON, SymbolConstant.LEFT_DIVIDE))) {
//                    throw new BusinessException(SysMenuExceptionEnum.MENU_PERMISSION_NOT_EXIST);
//                }
            }
        }

        // 如果是编辑菜单时候，pid和id不能一致，一致会导致无限递归
        if (b) {
            if (sysMenuParam.getId().equals(sysMenuParam.getPid())) {
                throw new BusinessException(SysMenuExceptionEnum.PID_CANT_EQ_ID);
            }
        }
        Long id = sysMenuParam.getId();
        String name = sysMenuParam.getName();
        String code = sysMenuParam.getCode();
        List<Object> params=new ArrayList<>();
        StringBuilder sb=new StringBuilder("select count(*) from sys_menu where status=0 and (name=? or code=?)");
        params.add(name);
        params.add(code);
        if (b) {
            sb.append(" and id <> ? ");
            params.add(id);
        }
        Integer countByName = (Integer) dbUtils.getValue(sb.toString(),Integer.class,params.toArray());

        if (countByName >= 1) {
            throw new BusinessException(SysMenuExceptionEnum.MENU_NAME_CODE_REPEAT);
        }
    }


    /**
     * 将SysMenu格式菜单转换为LoginMenuTreeNode菜单
     */
    private List<LoginMenuTreeNode> convertSysMenuToLoginMenu(List<SysMenu> sysMenuList) {
        List<LoginMenuTreeNode> antDesignMenuTreeNodeList = CollectionUtil.newArrayList();
        sysMenuList.forEach(sysMenu -> {
            LoginMenuTreeNode loginMenuTreeNode = new LoginMenuTreeNode();
            loginMenuTreeNode.setComponent(sysMenu.getComponent());
            loginMenuTreeNode.setId(sysMenu.getId());
            loginMenuTreeNode.setName(sysMenu.getCode());
            loginMenuTreeNode.setPath(sysMenu.getRouter());
            loginMenuTreeNode.setPid(sysMenu.getPid());
            LoginMenuTreeNode.Meta mateItem = new LoginMenuTreeNode().new Meta();
            mateItem.setIcon(sysMenu.getIcon());
            mateItem.setTitle(sysMenu.getName());
            mateItem.setLink(sysMenu.getLink());
            //是否可见
            mateItem.setShow(!"N".equals(sysMenu.getVisible()));
            //设置的首页，默认打开此链接
            loginMenuTreeNode.setRedirect(sysMenu.getRedirect());
            //是否是外链
            if (MenuOpenTypeEnum.OUTER.getCode().equals(sysMenu.getOpenType())) {
                //打开外链
                mateItem.setTarget("_blank");
                loginMenuTreeNode.setPath(sysMenu.getLink());
                loginMenuTreeNode.setRedirect(sysMenu.getLink());
            }
            loginMenuTreeNode.setMeta(mateItem);
            antDesignMenuTreeNodeList.add(loginMenuTreeNode);
        });
        return antDesignMenuTreeNodeList;
    }

}
